package ru.spbu.math.android.cbrf.dataprovider;

import android.util.Log;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import ru.spbu.math.android.cbrf.dataprovider.handlers.CurrencyDynamicSaxHandler;
import ru.spbu.math.android.cbrf.dataprovider.handlers.CurrencySaxHandler;
import ru.spbu.math.android.cbrf.dataprovider.handlers.MetalSaxHandler;
import ru.spbu.math.android.cbrf.dataprovider.web.CbrfUrlId;
import ru.spbu.math.android.cbrf.dataprovider.web.CbrfUrlIdStore;
import ru.spbu.math.android.cbrf.model.Rate;
import ru.spbu.math.android.cbrf.model.StockItem;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Class for getting data from CBRF
 *
 * @author Svetlana Zemlyanskaya
 */

public class CbrfData implements IProvider {

    private static final String TAG = "CbrfProvider";

    private final static String URL_ENG = "http://www.cbr.ru/scripts/XML_daily_eng.asp";
    private final static String URL_DYN = "http://www.cbr.ru/scripts/XML_dynamic.asp";
    private final static String URL_MET = "http://www.cbr.ru/scripts/xml_metall.asp";

    private final static String AUR = "AUR";
    private final static String ARG = "ARG";
    private final static String PT = "PT";
    private final static String PD = "PD";

    private static Map<Integer, String> resourceWidgetMap = new HashMap<Integer, String>();

//    @Override

    public boolean canGetRateByDate() {
        return true;
    }

//    @Override

    public Rate[] getDynamicRates(StockItem source, StockItem target,
                                  Date since, Date until) {
    	Log.d(TAG, "Get dynamic rates");
        if (!isUpdatableNow()) {
            return null;
        }
        if (!target.getCode().equals("RUB")) {
            return null;
        }

        List<Rate> rateList = new ArrayList<Rate>();
        if (isMetal(source)) {
            List<Rate> list = getDynamicMetalList(target, since, until);
            rateList = getMetalFromList(source, list);
        } else {
            rateList = getDynamicRateList(source, target, since, until);
        }
        
        if(rateList.size() == 0){
        	return null;
        }
        
        Rate[] rateArray = rateList.toArray(new Rate[]{});
        return rateArray;
    }

//    @Override

    public Rate getLatestRate(StockItem source, StockItem target) {
    	Log.d(TAG, "Get latest rate");
        if (!isUpdatableNow()) {
            return null;
        }
        if (!target.getCode().equals("RUB")) {
            return null;
        }

        List<Rate> rateList = new ArrayList<Rate>();
        if (isMetal(source)) {
            List<Rate> list = getMetalList(target);
            rateList = getMetalFromList(source, list);
        } else {
            rateList = getRate(source, target);
        }

        if (rateList.size() == 0) {
            return null;
        }

        Rate rate = rateList.get(0);
        return rate;
    }

//    @Override

    public Rate getRate(StockItem source, StockItem target, Date date) {
    	Log.d(TAG, "Get rate");
        if (!isUpdatableNow()) {
            return null;
        }
        if (!target.getCode().equals("RUB")) {
            return null;
        }

        List<Rate> rateList = new ArrayList<Rate>();
        if (isMetal(source)) {
            List<Rate> list = getMetalListByDate(target, date);
            rateList = getMetalFromList(source, list);
        } else {
            rateList = getRateByDate(source, target, date);
        }

        if (rateList.size() == 0) {
            return null;
        }
        
        return rateList.get(0);
    }

//    @Override

    public boolean isUpdatableNow() {
        try {
            URL url = new URL("http://www.cbr.ru");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.connect();
            return true;
        } catch (Exception e) {
        	Log.d(TAG, "Is not updatable");
            return false;
        }
    }

    private boolean isMetal(StockItem source) {
        if (source.getCode().equals(AUR) || source.getCode().equals(ARG)
                || source.getCode().equals(PT) || source.getCode().equals(PD)) {
            return true;
        }

        return false;
    }


    private List<Rate> getMetalFromList(StockItem source, List<Rate> rateList) {
        List<Rate> metalList = new ArrayList<Rate>();
        for (int i = 0; i < rateList.size(); i++) {
            String code = rateList.get(i).getSource().getCode();
            if (code.equals(source.getCode())) {
                metalList.add(rateList.get(i));
            }
        }
        return metalList;
    }

    /**
     * Return CurrencyRate list from CBFR on specify date
     *
     * @param date
     * @return List<Rate>
     */
    public List<Rate> getRateListByDate(StockItem target, Date date) {
        if (!isUpdatableNow()) {
            return null;
        }
        Date rateDate = date;
        CurrencySaxHandler handler = new CurrencySaxHandler();
        handler.setDate(rateDate);
        handler.setTarget(target);
        return getCbrfData(URL_ENG, rateDate, handler);
    }

    /**
     * Return CurrencyRate list from CBFR on current date
     *
     * @return List<Rate>
     */
    public List<Rate> getRateList(StockItem target) {
        if (!isUpdatableNow()) {
            return null;
        }
        Date rateDate = Calendar.getInstance().getTime();
        CurrencySaxHandler handler = new CurrencySaxHandler();
        handler.setDate(rateDate);
        handler.setTarget(target);
        return getCbrfData(URL_ENG, rateDate, handler);
    }

    private List<Rate> getCbrfData(String urlAddress, Date rateDate, CurrencySaxHandler handler) {

        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();

            SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
            String date = dateFormat.format(rateDate);
            URL url = new URL(urlAddress + "?date_req=" + date);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                return null;
            }

            InputSource courseXml = new InputSource(new InputStreamReader(conn.getInputStream(), "windows-1251"));
            parser.parse(courseXml, handler);
            List<Rate> currList = handler.getCurrency();
            return currList;

        } catch (MalformedURLException e) {
            Log.e(TAG, "MalformedURLException");
        } catch (IOException e) {
            Log.e(TAG, "IOException");
        } catch (SAXException e) {
            Log.e(TAG, "SAXException");
        } catch (ParserConfigurationException e) {
            Log.e(TAG, "ParserConfigurationException");
        }

        return null;
    }

    /**
     * Return specified rate from CBFR on specified date
     *
     * @param date, rateCode
     * @return List<Rate>
     */
    public List<Rate> getRateByDate(StockItem source, StockItem target, Date date) {
        if (!isUpdatableNow()) {
            return null;
        }
        CurrencyDynamicSaxHandler handler = new CurrencyDynamicSaxHandler();
        handler.setSource(source);
        handler.setTarget(target);
        return getDynamicCbrfData(URL_DYN, date, date, source, handler);
    }

    /**
     * Return specified rate from CBFR on current date
     *
     * @param source
     * @return List<Rate>
     */
    public List<Rate> getRate(StockItem source, StockItem target) {
        if (!isUpdatableNow()) {
            return null;
        }
        Date date = Calendar.getInstance().getTime();
        CurrencyDynamicSaxHandler handler = new CurrencyDynamicSaxHandler();
        handler.setSource(source);
        handler.setTarget(target);
        List<Rate> rateList = getDynamicCbrfData(URL_DYN, date, date, source, handler);
        
        if(rateList.size() == 0){
        	List<Rate> rate = new ArrayList<Rate>();
            boolean rateFound = false;
            Date dateRetry = date;

            while (!rateFound) {
                Calendar calendarRetry = Calendar.getInstance();
                calendarRetry.setTime(dateRetry);
                calendarRetry.add(Calendar.DAY_OF_MONTH, -1);
                dateRetry = calendarRetry.getTime();

                rate = getDynamicRateList( source, target, dateRetry, dateRetry);
                if (rate.size() != 0) {
                    rateFound = true;
                }
            }
            rateList.add(rate.get(0));
        }
        
        return rateList;
    }

    /**
     * Return dynamic of current rate from CBFR on period from startDate to endDate
     *
     * @param startDate, endDate, rateName
     * @return List<Rate>
     */
    public List<Rate> getDynamicRateList(StockItem source, StockItem target, Date startDate, Date endDate) {
        if (!isUpdatableNow()) {
            return null;
        }
        CurrencyDynamicSaxHandler handler = new CurrencyDynamicSaxHandler();
        handler.setSource(source);
        handler.setTarget(target);
        return getDynamicCbrfData(URL_DYN, startDate, endDate, source, handler);
    }

    private List<Rate> getDynamicCbrfData(String urlAddress, Date startDate, Date endDate, StockItem source, CurrencyDynamicSaxHandler handler) {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();

            SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
            String sDate = dateFormat.format(startDate);
            String eDate = dateFormat.format(endDate);

            CbrfUrlIdStore urlStore = CbrfUrlIdStore.Instance();
            CbrfUrlId urlId = new CbrfUrlId();
            urlId = urlStore.findByDataBaseCode(source.getCode());
            if (urlId == null) {
                return null;
            }

            URL url = new URL(urlAddress + "?date_req1=" + sDate + "&date_req2=" + eDate + "&VAL_NM_RQ=" + urlId.getUrlId());
            URLConnection conn = url.openConnection();

            InputSource courseXml = new InputSource(new InputStreamReader(conn.getInputStream(), "windows-1251"));
            parser.parse(courseXml, handler);
            List<Rate> currList = handler.getCurrency();
            return currList;

        } catch (MalformedURLException e) {
            Log.e(TAG, "MalformedURLException");
        } catch (IOException e) {
            Log.e(TAG, "IOException");
        } catch (SAXException e) {
            Log.e(TAG, "SAXException");
        } catch (ParserConfigurationException e) {
            Log.e(TAG, "ParserConfigurationException");
        }

        // TODO return null;
        return new ArrayList<Rate>();
    }

    /**
     * Return dynamic of current rate specific count from CBFR on period from startDate to endDate
     *
     * @param startDate, endDate, rateName
     * @return List<Rate>
     */
    public List<Rate> getDynamicCountRateList(StockItem source, StockItem target, Date startDate, Date endDate, int count) {
        if (!isUpdatableNow()) {
            return null;
        }
        return getDynamicCountCbrfData(URL_DYN, startDate, endDate, source, target, count);
    }

    private List<Rate> getDynamicCountCbrfData(String urlAddress, Date startDate, Date endDate, StockItem source, StockItem target, int count) {


        int yearDifference = endDate.getYear() - startDate.getYear();
        int monthDifference = endDate.getMonth() - startDate.getMonth();
        int dayDifference = endDate.getDay() - startDate.getDay();
        int difference = yearDifference * 365 + monthDifference * 30 + dayDifference;
        int intervalDays = difference / (count - 1);

        List<Rate> allCurrList = new ArrayList<Rate>();
        Date date = startDate;

        for (int i = 0; i < count; i++) {
            CbrfData cbrfData = new CbrfData();
            List<Rate> currList = new ArrayList<Rate>();
            cbrfData.getDynamicRateList(source, target, date, date);

            if (currList.size() == 0) {
                List<Rate> curr = new ArrayList<Rate>();
                boolean rateFound = false;
                Date dateRetry = date;

                while (!rateFound) {
                    Calendar calendarRetry = Calendar.getInstance();
                    calendarRetry.setTime(dateRetry);
                    calendarRetry.add(Calendar.DAY_OF_MONTH, 1);
                    dateRetry = calendarRetry.getTime();

                    curr = cbrfData.getDynamicRateList( source, target, dateRetry, dateRetry);
                    if (curr.size() != 0) {
                        rateFound = true;
                    }
                }
                currList.add(curr.get(0));
            }
            allCurrList.add(currList.get(0));

            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            calendar.add(Calendar.DAY_OF_MONTH, intervalDays);
            date = calendar.getTime();
        }
        return allCurrList;
    }

    /**
     * Return dynamic of metal rate from CBFR on period from startDate to endDate
     *
     * @param startDate, endDate, rateName
     * @return List<Rate>
     */
    public List<Rate> getDynamicMetalList(StockItem target, Date startDate, Date endDate) {
        if (!isUpdatableNow()) {
            return null;
        }
        MetalSaxHandler handler = new MetalSaxHandler();
        handler.setTarget(target);
        return getDynamicMetalCbrfData(URL_MET, startDate, endDate, handler);
    }

    /**
     * Return metal rate from CBFR on specified date
     *
     * @param date, endDate, rateName
     * @return List<Rate>
     */
    public List<Rate> getMetalListByDate(StockItem target, Date date) {
        if (!isUpdatableNow()) {
            return null;
        }
        MetalSaxHandler handler = new MetalSaxHandler();
        handler.setTarget(target);
        return getDynamicMetalCbrfData(URL_MET, date, date, handler);
    }

    /**
     * Return metal rate from CBFR on current date
     *
     * @return List<Rate>
     */
    public List<Rate> getMetalList(StockItem target) {
        if (!isUpdatableNow()) {
            return null;
        }
        MetalSaxHandler handler = new MetalSaxHandler();
        handler.setTarget(target);
        Date date = Calendar.getInstance().getTime();
        
        List<Rate> rateList = getDynamicMetalCbrfData(URL_MET, date, date, handler);
        
        if(rateList.size() == 0){
        	List<Rate> rate = new ArrayList<Rate>();
            boolean rateFound = false;
            Date dateRetry = date;

            while (!rateFound) {
                Calendar calendarRetry = Calendar.getInstance();
                calendarRetry.setTime(dateRetry);
                calendarRetry.add(Calendar.DAY_OF_MONTH, -1);
                dateRetry = calendarRetry.getTime();

                rate = getMetalListByDate(target, dateRetry);
                if (rate.size() != 0) {
                    rateFound = true;
                }
            }
            for(int i = 0; i < rate.size(); i++){
            	rateList.add(rate.get(i));
            }
        }
        
        return rateList;
    }

    private List<Rate> getDynamicMetalCbrfData(String urlAddress, Date startDate, Date endDate, MetalSaxHandler handler) {

        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();

            SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
            String sDate = dateFormat.format(startDate);
            String eDate = dateFormat.format(endDate);

            URL url = new URL(urlAddress + "?date_req1=" + sDate + "&date_req2=" + eDate);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                return null;
            }

            InputSource courseXml = new InputSource(new InputStreamReader(conn.getInputStream(), "windows-1251"));
            parser.parse(courseXml, handler);
            List<Rate> currList = handler.getCurrency();
            return currList;

        } catch (MalformedURLException e) {
            Log.e(TAG, "MalformedURLException");
        } catch (IOException e) {
            Log.e(TAG, "IOException");
        } catch (SAXException e) {
            Log.e(TAG, "SAXException");
        } catch (ParserConfigurationException e) {
            Log.e(TAG, "ParserConfigurationException");
        }
        return null;
    }
}
